package com.system.boss.openssl.service;

import java.io.File;
import java.io.FileInputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.system.boss.openssl.enums.SysOpensslCerStatusEnum;
import com.system.boss.openssl.enums.SysOpensslCerTypeEnum;
import com.system.boss.openssl.mapper.SysOpensslMapper;
import com.system.boss.openssl.model.SysOpenssl;
import com.system.boss.openssl.model.SysOpensslExample;
import com.system.core.results.ResultBase;
import com.system.core.results.SimpleResultBuilder;
import com.system.core.view.layui.table.TableViewReqDto;
import com.system.core.view.layui.table.TableViewRspDto;
import com.system.core.view.layui.table.TableViewUtil;
import com.system.core.view.layui.tree.TreeNode;

import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class OpenSSLService {

	@Autowired
	private SysOpensslMapper sysOpensslMapper;

	@Autowired
	private OpenSSLGenTool openSSLGenTool;
	
	/**
	 * 
	 * @param sysOpenssl
	 * @return
	 * @throws Exception 
	 */
	public ResultBase userCertAdd(SysOpenssl sysOpenssl) throws Exception {
		SysOpenssl ca;
		switch (sysOpenssl.getCerType()) {
		case ROOTCA:
			openSSLGenTool.makeRootCA(sysOpenssl);
			sysOpenssl.setCerXpath(sysOpenssl.getCerSerial());
			break;
		case SUBCA:
			ca = sysOpensslMapper.selectByPrimaryKey(sysOpenssl.getCerPid());
			openSSLGenTool.makeSubCA(sysOpenssl,ca);
			sysOpenssl.setCerXpath(ca.getCerSerial()+":"+sysOpenssl.getCerSerial());
			break;
		case USER:
			ca = sysOpensslMapper.selectByPrimaryKey(sysOpenssl.getCerPid());
			openSSLGenTool.makeUserCer(sysOpenssl, ca);
			sysOpenssl.setCerXpath(ca.getCerSerial()+":"+sysOpenssl.getCerSerial());
			break;
		default:
			break;
		}
		
		sysOpensslMapper.insertSelective(sysOpenssl);
		return new ResultBase(true, "添加成功");
	}

	public SysOpenssl userCertGet(SysOpenssl sysOpenssl) {
		return sysOpensslMapper.selectByPrimaryKey(sysOpenssl.getCerId());
	}

	public int userCertDelete(SysOpenssl sysOpenssl) {
		return sysOpensslMapper.deleteByPrimaryKey(sysOpenssl.getCerId());
	}

	/**
	 * 获取用户证书列表<br>
	 * 1) 通过父ID获取列表<br>
	 * 2) 如果父ID为空则默认为0
	 * 
	 * @param page   分页信息
	 * @param filter 查询条件
	 * @return
	 */
	public TableViewRspDto<SysOpenssl> userCertList(TableViewReqDto page, SysOpenssl filter) {

		SysOpensslExample example = new SysOpensslExample();
		example.createCriteria().andCerTypeEqualTo(SysOpensslCerTypeEnum.USER)
				.andCerPidEqualTo(filter.getCerPid() == null ? 0L : filter.getCerPid());

		Page<SysOpenssl> pager = PageHelper.startPage(page.getPage(), page.getLimit());
		List<SysOpenssl> list = sysOpensslMapper.selectByExample(example);

		return TableViewUtil.listCoverter(list, pager);
	}
	
	public File makeCrl(Long cerId) {
		SysOpenssl cainfo = sysOpensslMapper.selectByPrimaryKey(cerId);
		return new File(OpenSSLGenTool.homedir+openSSLGenTool.makeCrl(cainfo));
	}
	
	public ResultBase revokeCer(Long caid , Long cerid) {
		
		SysOpenssl revokecer = sysOpensslMapper.selectByPrimaryKey(cerid);
		SysOpenssl ca = sysOpensslMapper.selectByPrimaryKey(caid);
		
		openSSLGenTool.cerRevoke(
				revokecer.getWorkDir()+revokecer.getCerPath(), ca
		);
		
		String tmpcer = openSSLGenTool.extractCerFromP12(revokecer.getP12Path(), revokecer.getCerPasswd());
		
		openSSLGenTool.cerRevoke(
				revokecer.getWorkDir()+tmpcer, ca
		);
		
		File tmpcerFile = new File(revokecer.getWorkDir(),tmpcer);
		if(tmpcerFile.exists()) {
			tmpcerFile.delete();
		}
		
		revokecer.setCerStatus(SysOpensslCerStatusEnum.INVOKED);
		
		sysOpensslMapper.updateByPrimaryKeySelective(revokecer);
		
		return SimpleResultBuilder.success("\""+revokecer.getCommonName()+"\"吊销成功！");
	}
	
	public void nginxCerDownload(Long cerid,OutputStream outstream) throws Exception {
		SysOpenssl sysOpenssl = sysOpensslMapper.selectByPrimaryKey(cerid);
		
		ZipOutputStream zipos = new ZipOutputStream(outstream);
		
		zipos.putNextEntry(new ZipEntry(sysOpenssl.getCommonName()+"_server_cer_public.crt"));
		FileInputStream fis = new FileInputStream(sysOpenssl.getWorkDir()+sysOpenssl.getCerPath());
		IOUtils.copy(fis, zipos);
		fis.close();
		
		zipos.putNextEntry(new ZipEntry(sysOpenssl.getCommonName()+"_server_key_nopasswd.pem"));
		fis = new FileInputStream(sysOpenssl.getWorkDir()+sysOpenssl.getKeyWithoutPasswdPath());
		IOUtils.copy(fis, zipos);
		fis.close();
		
		String rootcaid = sysOpenssl.getCerXpath().split(":")[0];
		SysOpensslExample caexample = new SysOpensslExample();
		caexample.createCriteria().andCerSerialEqualTo(rootcaid);
		List<SysOpenssl> list = sysOpensslMapper.selectByExample(caexample);
		SysOpenssl caobj = list.get(0);
		zipos.putNextEntry(new ZipEntry(caobj.getCommonName()+"_ca_cer_public.crt"));
		fis = new FileInputStream(caobj.getWorkDir()+caobj.getCerPath());
		IOUtils.copy(fis, zipos);
		fis.close();
		
		zipos.putNextEntry(new ZipEntry("ssl_crl.crl"));
		
		File crlfile = makeCrl(caobj.getCerId());
		fis = new FileInputStream(crlfile);
		IOUtils.copy(fis, zipos);
		fis.close();
		
		zipos.flush();
		zipos.close();
		
		outstream.flush();
		outstream.close();
	}
	
	/**
	 * 根据ID获取证书
	 * @param certId
	 * @return
	 */
	public SysOpenssl getCertById(Long certId) {
		return sysOpensslMapper.selectByPrimaryKey(certId);
	}
	
	/**
	 * 通过父ID获取证书链
	 * 
	 * @param certId
	 * @return
	 */
	public List<TreeNode> getNodeByParentId(Long certId) {

		List<SysOpensslCerTypeEnum> certype = new ArrayList<SysOpensslCerTypeEnum>();
		certype.add(SysOpensslCerTypeEnum.ROOTCA);
		certype.add(SysOpensslCerTypeEnum.SUBCA);

		List<TreeNode> rootnodes = new ArrayList<TreeNode>();

		SysOpensslExample example = new SysOpensslExample();
		example.createCriteria().andCerTypeIn(certype).andCerPidEqualTo(certId);
		List<SysOpenssl> opensslList = sysOpensslMapper.selectByExample(example);

		SysOpensslExample example2 = new SysOpensslExample();
		example2.createCriteria().andCerTypeIn(certype);
		List<SysOpenssl> allList = sysOpensslMapper.selectByExample(example2);

		for (SysOpenssl sysOpenssl : opensslList) {

			TreeNode rootnode = new TreeNode();
			rootnode.setTitle(sysOpenssl.getCommonName());
			rootnode.setId(sysOpenssl.getCerId().intValue());
			rootnode.setSpread(true);

			rootnodes.add(nodebuild(rootnode, allList));
		}

		return rootnodes;
	}

	private TreeNode nodebuild(TreeNode pnode, List<SysOpenssl> list) {
		for (SysOpenssl nodedo : list) {
			if (nodedo.getCerPid() != 0 && nodedo.getCerPid() == pnode.getId()) {
				TreeNode node = new TreeNode();
				node.setId(nodedo.getCerId().intValue());
				node.setTitle(nodedo.getCommonName());
				node.setSpread(true);
				pnode.getChildren().add(node);
				nodebuild(node, list);
			}
		}
		return pnode;
	}

}